package com.triplelink.builder.query;

import java.lang.reflect.Field;
import java.util.LinkedList;
import java.util.Queue;

import com.triplelink.annotations.DatatypeProperty;
import com.triplelink.annotations.ObjectProperty;
import com.triplelink.builder.query.evalueter.DateTimeEvalueter;
import com.triplelink.builder.query.evalueter.ObjectEvalueter;
import com.triplelink.builder.query.evalueter.StringEvalueter;
import com.triplelink.context.Connector;
import com.triplelink.exception.TripleLinkException;
import com.triplelink.util.ClassTypeUtil;
import com.triplelink.util.EntityUtil;

public class InsertQueryBuilder
{
	public static QueryTL build(Object entity, String abox, Connector connector)
	{
		QueryTL query = QueryTL.create(connector);
		
		try
		{
			query
			.append("INSERT \n")
			.append("{ \n")
			.append("	GRAPH ?abox \n")
			.append("	{ \n")
			
			.plusTab()
			.plusTab();
			
			buildTriples(entity, query);
	
			query
			.append("	} \n")
			.append("} \n")
			.append("WHERE { } \n")
			
			.saveLabel("abox", abox);
			
			return query;
		}
		catch (IllegalArgumentException e)
		{
			throw new TripleLinkException(e);
		}
		catch (IllegalAccessException e)
		{
			throw new TripleLinkException(e);
		}
	}
	
	private static void buildTriples(Object entity, QueryTL query) throws IllegalArgumentException, IllegalAccessException
	{
		Queue<Object> forDiscovery = new LinkedList<Object>();
		
		forDiscovery.offer(entity);
		
		while (!forDiscovery.isEmpty())
		{
			Object currentEntity = forDiscovery.poll();
			
			String id = EntityUtil.getIdOrCreate(currentEntity);
			
			ClassTypeUtil.putClassType(currentEntity, id, query);
			
			putProperties(currentEntity, id, query, forDiscovery);
			
			query.informDiscovered(id, currentEntity);
			
			if (!forDiscovery.isEmpty())
				query.append("\n");
		}
	}
	
	static void putProperties(Object currentEntity, String id, QueryTL query, Queue<Object> forDiscovery) throws IllegalArgumentException, IllegalAccessException
	{
		Class<?> currentClass = currentEntity.getClass();
		
		while (currentClass != null)
		{
			for (Field field : currentClass.getDeclaredFields())
			{
				field.setAccessible(true);
				
				DatatypeProperty datatypeProperty = field.getAnnotation(DatatypeProperty.class);
				
				if (datatypeProperty != null)
				{
					putValue(field, currentEntity, id, datatypeProperty, query);
					continue;
				}
				
				ObjectProperty objectProperty = field.getAnnotation(ObjectProperty.class);
				
				if (objectProperty != null)
				{
					putValue(field, currentEntity, id, objectProperty, query, forDiscovery);
					continue;
				}
			}
			
			currentClass = currentClass.getSuperclass();
		}
	}
	
	static void putValue(Field field, Object currentEntity, String id, DatatypeProperty datatypeProperty, QueryTL query) throws IllegalArgumentException, IllegalAccessException
	{
		switch (datatypeProperty.range())
		{
			case STRING:
				StringEvalueter.putValue(field, currentEntity, id, query);
				break;
				
			case DATE_TIME:
				DateTimeEvalueter.putValue(field, currentEntity, id, query);
				break;
	
			default:
				break;
		}
	}
	
	static void putValue(Field field, Object currentEntity, String id, ObjectProperty objectProperty, QueryTL query, Queue<Object> forDiscovery) throws IllegalArgumentException, IllegalAccessException
	{
		if (!objectProperty.cascadeSave())
			return;
		
		ObjectEvalueter.evaluete(field, currentEntity, id, query, forDiscovery);
	}
}